package NIO_AIO_Netty;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @program: planet-company
 * @description:
 * @author: jyz
 * @create: 2019-09-21 14:24
 **/
public class NIOServerTest {

    public static void main(String[] args) throws IOException {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress("127.0.0.1",8888));
        ssc.configureBlocking(false);

        Selector selector = Selector.open();
        //服务在selector注册连接事件
        ssc.register(selector,SelectionKey.OP_ACCEPT);

        //轮询
        while (true){
            selector.select();

            //每一个key相当于Server面板上每一个插头
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> it = keys.iterator();
            while (it.hasNext()){
                SelectionKey selectionKey = it.next();
                it.remove();//处理完后一定要进行remove，不然会重复处理
                handle(selectionKey);
            }
        }

    }

    static void handle(SelectionKey key) throws IOException {
        //如果key是连接事件
        if (key.isAcceptable()){
            try {
                //处理一个连接，就是插上一个插头
                ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                SocketChannel sc = ssc.accept();
                sc.configureBlocking(false);

                //这个插头又在Selector注册读取时间
                sc.register(key.selector(),SelectionKey.OP_READ);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else if (key.isReadable()){

            //注意注意：处理：如果是Reactor模式的话，这里是直接创建一个线程去处理了，
            // 就不在当前线程处理了，也就意味着当前线程就解放了，可以去处理其他连接操作了

            //如果是读取事件
            //也就是说 现在Client和Server中间的通道，是可以做任何时间的，而且是双向的
            SocketChannel sc = null;
            try {
                //处理一个连接，就是插上一个插头
                sc = (SocketChannel)key.channel();
                //通道读取数据变成从Buffer中读取了，不是一个字节 一个字节的了，这样子效率比较高
                ByteBuffer buffer = ByteBuffer.allocate(512);
                buffer.clear();
                int len = sc.read(buffer);
                if (len != 1){
                    System.out.println("数据异常，为空");
                }

                ByteBuffer byteBufferToWrite = ByteBuffer.wrap("HelloClient my name is Server".getBytes());
                sc.write(byteBufferToWrite);
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                sc.close();
            }

        }
    }

    /**
     * 1、NIO是通过Selector轮询进行连接、读取、写入数据管理的
     * 2、数据的读取或写入  是直接从Buffer的，不是在想之前一个字节 一个字节了
     * 3、此时客户端和服务端的通道是双向的
     *
     * 注意IO处理模式：分为单例处理模式和Reactor处理模式，，我们一般都是用Reactor模式的
     * 就是 一个负责连接，一个负责处理，，，高级是负责处理的业务的线程准备一个线程池，随时待命
     *
     *
     */
}
